package com.generator.service;

import com.generator.model.ColumnInfo;
import com.generator.model.TableInfo;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import jakarta.annotation.PostConstruct;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 基于FreeMarker的CRUD代码生成器服务
 *
 * @author Generator
 * @since 2024-01-01
 */
@Service
public class FreemarkerCrudGenerator {

    private Configuration freemarkerConfig;

    private static final String DEFAULT_PACKAGE = "com.example";
    private static final String DEFAULT_AUTHOR = "Generator";
    private static final String OUTPUT_DIR = "./generated-code";

    @PostConstruct
    public void initFreemarker() throws IOException {
        freemarkerConfig = new Configuration(Configuration.VERSION_2_3_31);
        freemarkerConfig.setClassForTemplateLoading(this.getClass(), "/templates/code");
        freemarkerConfig.setDefaultEncoding("UTF-8");
    }

    /**
     * 生成CRUD代码
     *
     * @param tableInfo   表信息
     * @param packageName 包名
     * @param author      作者
     * @return 生成的代码文件路径映射
     */
    public Map<String, String> generateCrudCode(TableInfo tableInfo, String packageName, String author) {
        return generateCrudCode(tableInfo, packageName, author, OUTPUT_DIR);
    }

    /**
     * 生成CRUD代码
     *
     * @param tableInfo   表信息
     * @param packageName 包名
     * @param author      作者
     * @param outputDir   输出目录
     * @return 生成的代码文件路径映射
     */
    public Map<String, String> generateCrudCode(TableInfo tableInfo, String packageName, String author, String outputDir) {
        Map<String, String> generatedFiles = new HashMap<>();

        // 设置基本信息
        if (packageName == null || packageName.trim().isEmpty()) {
            packageName = DEFAULT_PACKAGE;
        }
        if (author == null || author.trim().isEmpty()) {
            author = DEFAULT_AUTHOR;
        }

        tableInfo.setPackageName(packageName);
        tableInfo.setAuthor(author);

        try {
            // 创建输出目录
            createOutputDirectories(packageName, tableInfo.getEntityName(), outputDir);

            // 生成实体类
            String entityCode = generateEntity(tableInfo, packageName, author);
            generatedFiles.put("Entity", entityCode);
            writeToFile(entityCode, getEntityPath(packageName, tableInfo.getEntityName(), outputDir));

            // 生成Mapper接口
            String mapperCode = generateMapper(tableInfo, packageName, author);
            generatedFiles.put("Mapper", mapperCode);
            writeToFile(mapperCode, getMapperPath(packageName, tableInfo.getEntityName(), outputDir));

            // 生成Mapper XML
            String mapperXmlCode = generateMapperXml(tableInfo, packageName, author);
            generatedFiles.put("MapperXml", mapperXmlCode);
            writeToFile(mapperXmlCode, getMapperXmlPath(packageName, tableInfo.getEntityName(), outputDir));

            // 生成Service接口
            String serviceCode = generateService(tableInfo, packageName, author);
            generatedFiles.put("Service", serviceCode);
            writeToFile(serviceCode, getServicePath(packageName, tableInfo.getEntityName(), outputDir));

            // 生成Service实现类
            String serviceImplCode = generateServiceImpl(tableInfo, packageName, author);
            generatedFiles.put("ServiceImpl", serviceImplCode);
            writeToFile(serviceImplCode, getServiceImplPath(packageName, tableInfo.getEntityName(), outputDir));

            // 生成Controller
            String controllerCode = generateController(tableInfo, packageName, author);
            generatedFiles.put("Controller", controllerCode);
            writeToFile(controllerCode, getControllerPath(packageName, tableInfo.getEntityName(), outputDir));

        } catch (Exception e) {
            throw new RuntimeException("生成代码失败: " + e.getMessage(), e);
        }

        return generatedFiles;
    }

    /**
     * 生成实体类代码
     */
    private String generateEntity(TableInfo tableInfo, String packageName, String author) throws IOException, TemplateException {
        Map<String, Object> dataModel = createDataModel(tableInfo, packageName, author);
        return processTemplate("entity.java.ftl", dataModel);
    }

    /**
     * 生成Mapper接口代码
     */
    private String generateMapper(TableInfo tableInfo, String packageName, String author) throws IOException, TemplateException {
        Map<String, Object> dataModel = createDataModel(tableInfo, packageName, author);
        return processTemplate("mapper.java.ftl", dataModel);
    }

    /**
     * 生成Mapper XML代码
     */
    private String generateMapperXml(TableInfo tableInfo, String packageName, String author) throws IOException, TemplateException {
        Map<String, Object> dataModel = createDataModel(tableInfo, packageName, author);
        return processTemplate("mapper.xml.ftl", dataModel);
    }

    /**
     * 生成Service接口代码
     */
    private String generateService(TableInfo tableInfo, String packageName, String author) throws IOException, TemplateException {
        Map<String, Object> dataModel = createDataModel(tableInfo, packageName, author);
        return processTemplate("service.java.ftl", dataModel);
    }

    /**
     * 生成Service实现类代码
     */
    private String generateServiceImpl(TableInfo tableInfo, String packageName, String author) throws IOException, TemplateException {
        Map<String, Object> dataModel = createDataModel(tableInfo, packageName, author);
        return processTemplate("serviceImpl.java.ftl", dataModel);
    }

    /**
     * 生成Controller代码
     */
    private String generateController(TableInfo tableInfo, String packageName, String author) throws IOException, TemplateException {
        Map<String, Object> dataModel = createDataModel(tableInfo, packageName, author);
        return processTemplate("controller.java.ftl", dataModel);
    }

    /**
     * 创建FreeMarker数据模型
     */
    private Map<String, Object> createDataModel(TableInfo tableInfo, String packageName, String author) {
        Map<String, Object> dataModel = new HashMap<>();

        // 基本信息
        dataModel.put("author", author);
        dataModel.put("date", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        dataModel.put("entity", tableInfo.getEntityName());

        // 包信息
        Map<String, String> packageMap = new HashMap<>();
        packageMap.put("Entity", packageName + ".entity");
        packageMap.put("Mapper", packageName + ".mapper");
        packageMap.put("Service", packageName + ".service");
        packageMap.put("ServiceImpl", packageName + ".service.impl");
        packageMap.put("Controller", packageName + ".controller");
        dataModel.put("package", packageMap);

        // 表信息
        Map<String, Object> table = new HashMap<>();
        table.put("name", tableInfo.getTableName());
        table.put("comment", tableInfo.getTableComment() != null ? tableInfo.getTableComment() : tableInfo.getEntityName());
        table.put("entityPath", tableInfo.getEntityNameLowerCase());
        table.put("mapperName", tableInfo.getEntityName() + "Mapper");
        table.put("serviceName", tableInfo.getEntityName() + "Service");
        table.put("serviceImplName", tableInfo.getEntityName() + "ServiceImpl");
        table.put("controllerName", tableInfo.getEntityName() + "Controller");

        // 字段信息
        List<Map<String, Object>> fields = new ArrayList<>();
        List<Map<String, Object>> commonFields = new ArrayList<>();
        boolean hasTimeField = false;

        for (ColumnInfo column : tableInfo.getColumns()) {
            Map<String, Object> field = new HashMap<>();
            field.put("name", column.getColumnName());
            // 兼容模板中同时使用 name 与 columnName 的场景
            field.put("columnName", column.getColumnName());
            field.put("propertyName", column.getPropertyName());
            field.put("propertyType", column.getJavaType());
            field.put("comment", column.getComment() != null ? column.getComment() : column.getPropertyName());
            field.put("keyFlag", column.isPrimaryKey());
            field.put("keyIdentityFlag", column.isAutoIncrement());
            field.put("convert", !column.getColumnName().equals(column.getPropertyName()));

            if ("LocalDateTime".equals(column.getJavaType()) || "LocalDate".equals(column.getJavaType())) {
                hasTimeField = true;
            }

            fields.add(field);

            // 公共字段（如创建时间、更新时间等）
            if ("create_time".equals(column.getColumnName()) || "update_time".equals(column.getColumnName()) ||
                    "create_by".equals(column.getColumnName()) || "update_by".equals(column.getColumnName())) {
                commonFields.add(field);
            }
        }

        table.put("fields", fields);
        table.put("commonFields", commonFields);

        // 生成字段名列表
        StringBuilder fieldNames = new StringBuilder();
        for (int i = 0; i < fields.size(); i++) {
            if (i > 0) fieldNames.append(", ");
            fieldNames.append(((Map<String, Object>) fields.get(i)).get("name"));
        }
        table.put("fieldNames", fieldNames.toString());

        dataModel.put("table", table);
        dataModel.put("hasTimeField", hasTimeField);

        // MyBatis-Plus相关配置
        dataModel.put("superMapperClass", "BaseMapper");
        dataModel.put("superServiceClass", "IService");
        dataModel.put("superServiceImplClass", "ServiceImpl");
        dataModel.put("superServiceClassPackage", "com.baomidou.mybatisplus.extension.service.IService");
        dataModel.put("superServiceImplClassPackage", "com.baomidou.mybatisplus.extension.service.impl.ServiceImpl");

        // 导入包配置
        List<String> importMapperFrameworkPackages = Arrays.asList(
                "com.baomidou.mybatisplus.core.mapper.BaseMapper",
                "org.apache.ibatis.annotations.Mapper"
        );
        dataModel.put("importMapperFrameworkPackages", importMapperFrameworkPackages);
        dataModel.put("importMapperJavaPackages", new ArrayList<>());

        // 其他配置
        dataModel.put("kotlin", false);
        dataModel.put("generateService", true);
        dataModel.put("restControllerStyle", true);
        dataModel.put("baseResultMap", true);
        dataModel.put("baseColumnList", true);
        dataModel.put("enableCache", false);
        dataModel.put("mapperMethodList", new ArrayList<>());

        return dataModel;
    }

    /**
     * 处理FreeMarker模板
     */
    private String processTemplate(String templateName, Map<String, Object> dataModel) throws IOException, TemplateException {
        Template template = freemarkerConfig.getTemplate(templateName);
        StringWriter writer = new StringWriter();
        template.process(dataModel, writer);
        return writer.toString();
    }

    /**
     * 创建输出目录（带路径校验）
     */
    private void createOutputDirectories(String packageName, String entityName, String outputDir) {
        // 1. 校验输出路径是否为空
        if (outputDir == null || outputDir.trim().isEmpty()) {
            throw new IllegalArgumentException("输出路径不能为空！");
        }

        File baseOutputDir = new File(outputDir);

        // 2. 校验输出路径是否存在且可写
        if (!baseOutputDir.exists()) {
            if (!baseOutputDir.mkdirs()) {
                throw new IllegalStateException("无法创建输出目录：" + baseOutputDir.getAbsolutePath());
            }
        } else if (!baseOutputDir.isDirectory()) {
            throw new IllegalArgumentException("输出路径不是有效的目录：" + baseOutputDir.getAbsolutePath());
        } else if (!baseOutputDir.canWrite()) {
            throw new IllegalStateException("输出目录不可写：" + baseOutputDir.getAbsolutePath());
        }

        // 3. 生成子目录结构
        String baseDir = outputDir + "/" + packageName.replace(".", "/");
        String[] dirs = {
                baseDir + "/entity",
                baseDir + "/mapper",
                baseDir + "/service",
                baseDir + "/service/impl",
                baseDir + "/controller",
                outputDir + "/resources/mapper"
        };

        for (String dir : dirs) {
            File file = new File(dir);
            if (!file.exists() && !file.mkdirs()) {
                throw new IllegalStateException("无法创建目录：" + file.getAbsolutePath());
            }
        }

        System.out.println("✅ 输出目录创建完成：" + new File(baseDir).getAbsolutePath());
    }


    /**
     * 写入文件
     */
    private void writeToFile(String content, String filePath) throws IOException {
        File file = new File(filePath);
        file.getParentFile().mkdirs();

        try (FileWriter writer = new FileWriter(file)) {
            writer.write(content);
        }
    }

    // 获取各种文件路径的方法
    private String getEntityPath(String packageName, String entityName) {
        return getEntityPath(packageName, entityName, OUTPUT_DIR);
    }

    private String getEntityPath(String packageName, String entityName, String outputDir) {
        return outputDir + "/" + packageName.replace(".", "/") + "/entity/" + entityName + ".java";
    }

    private String getMapperPath(String packageName, String entityName) {
        return getMapperPath(packageName, entityName, OUTPUT_DIR);
    }

    private String getMapperPath(String packageName, String entityName, String outputDir) {
        return outputDir + "/" + packageName.replace(".", "/") + "/mapper/" + entityName + "Mapper.java";
    }

    private String getMapperXmlPath(String packageName, String entityName) {
        return getMapperXmlPath(packageName, entityName, OUTPUT_DIR);
    }

    private String getMapperXmlPath(String packageName, String entityName, String outputDir) {
        return outputDir + "/resources/mapper/" + entityName + "Mapper.xml";
    }

    private String getServicePath(String packageName, String entityName) {
        return getServicePath(packageName, entityName, OUTPUT_DIR);
    }

    private String getServicePath(String packageName, String entityName, String outputDir) {
        return outputDir + "/" + packageName.replace(".", "/") + "/service/" + entityName + "Service.java";
    }

    private String getServiceImplPath(String packageName, String entityName) {
        return getServiceImplPath(packageName, entityName, OUTPUT_DIR);
    }

    private String getServiceImplPath(String packageName, String entityName, String outputDir) {
        return outputDir + "/" + packageName.replace(".", "/") + "/service/impl/" + entityName + "ServiceImpl.java";
    }

    private String getControllerPath(String packageName, String entityName) {
        return getControllerPath(packageName, entityName, OUTPUT_DIR);
    }

    private String getControllerPath(String packageName, String entityName, String outputDir) {
        return outputDir + "/" + packageName.replace(".", "/") + "/controller/" + entityName + "Controller.java";
    }
}